package com.bg7yoz.ft8cn.icom;
/**
 * 控制流的基本类。
 *
 * @author BGY70Z
 * @date 2023-08-26
 */

import android.util.Log;

import java.net.DatagramPacket;
import java.util.Timer;
import java.util.TimerTask;

public class ControlUdp extends IcomUdpBase {
    private static final String TAG = "ControlUdp";
    public final String APP_NAME = "FT8CN";

    //与采样率有关，每20ms发送的样本数12000/50=240=F0，实际字节数是（16bit），还要乘以2，也就是480字节


    public Timer tokenTimer;//续订令牌的时钟

    public String userName;
    public String password;
    public String rigName = "";
    public String audioName = "";
    public byte[] rigMacAddress = new byte[6];//0xA8、0x90包中提供
    public String connectionMode = "";

    public boolean gotAuthOK = false;//token认证通过了
    public boolean isAuthenticated = false;//登录成功
    public boolean rigIsBusy = false;

    public IcomCivUdp civUdp;
    public AudioUdp audioUdp;


    public ControlUdp(String userName, String password, String remoteIp, int remotePort) {
        udpStyle = IcomUdpStyle.ControlUdp;
        this.userName = userName;
        this.password = password;

        this.rigIp = remoteIp;
        this.rigPort = remotePort;

    }


    @Override
    public void onDataReceived(DatagramPacket packet, byte[] data) {
        // 父类默认处理一下数据包：
        // 控制包0x10（CMD_I_AM_HERE、CMD_RETRANSMIT），
        // ping包0x15
        // 变长包：RETRANSMIT包，type=IComPacketTypes.CMD_RETRANSMIT
        super.onDataReceived(packet, data);
        switch (data.length) {
            case IComPacketTypes.CONTROL_SIZE://在父类中已经实现0x04,0x01指令
                if (IComPacketTypes.ControlPacket.getType(data) == IComPacketTypes.CMD_I_AM_HERE) {
                    rigIp = packet.getAddress().getHostAddress();
                }
                //如果电台回复I'm ready,就发起login
                if (IComPacketTypes.ControlPacket.getType(data) == IComPacketTypes.CMD_I_AM_READY) {
                    sendLoginPacket();//电台准备好了，申请登录 0x80包
                    startIdleTimer();//打开发送空包时钟
                }
                break;
            case IComPacketTypes.TOKEN_SIZE://处理令牌的续订之类的事情
                onReceiveTokenPacket(data);
                break;
            case IComPacketTypes.STATUS_SIZE://0x50电台回复我它的参数：CivPort,AudioPort等
                onReceiveStatusPacket(data);
                break;
            case IComPacketTypes.LOGIN_RESPONSE_SIZE://0x60电台回复登录的请求
                onReceiveLoginResponse(data);
                break;
            case IComPacketTypes.CONNINFO_SIZE://电台会回复2次0x90包，区别在于busy字段
                onReceiveConnInfoPacket(data);
                break;
            case IComPacketTypes.CAP_CAPABILITIES_SIZE://0xA8数据包,返回civ地址
                byte[] audioCap = IComPacketTypes.CapCapabilitiesPacket.getRadioCapPacket(data, 0);
                if (audioCap != null) {
                    civUdp.supportTX = IComPacketTypes.RadioCapPacket.getSupportTX(audioCap);
                    civUdp.civAddress = IComPacketTypes.RadioCapPacket.getCivAddress(audioCap);
                    audioName = IComPacketTypes.RadioCapPacket.getAudioName(audioCap);
                }
                break;
        }
    }


    /**
     * 处理电台发送过来的connInfo（0x90）数据包，电台发送0x90包有两次，第一次busy=0,第二次busy=1。
     * 在0x90数据包中取macAddress，电台名称
     * 这部分留给IcomControlUdp和XieGuControlUdp来处理
     * @param data 0x90数据包
     */
    public void onReceiveConnInfoPacket(byte[] data) {
    }


    /**
     * 处理电台回复登录数据包
     *
     * @param data 0x60数据包
     */
    public void onReceiveLoginResponse(byte[] data) {
        if (IComPacketTypes.ControlPacket.getType(data) == 0x01) return;
        connectionMode = IComPacketTypes.LoginResponsePacket.getConnection(data);
        Log.d(TAG, "connection mode:" + connectionMode);
        if (IComPacketTypes.LoginResponsePacket.authIsOK(data)) {//errorCode=0x00,认证成功
            Log.d(TAG, "onReceiveLoginResponse: Login succeed!");
            if (!isAuthenticated) {
                rigToken = IComPacketTypes.LoginResponsePacket.getToken(data);
                Log.d(TAG, "onReceiveLoginResponse: send token confirm 0x02");
                sendTokenPacket(IComPacketTypes.TOKEN_TYPE_CONFIRM);//发送令牌确认包 0x40
                startTokenTimer();//启动令牌续订时钟
                isAuthenticated = true;
            }
        }
        if (onStreamEvents != null) {//触发认证事件
            onStreamEvents.OnLoginResponse(IComPacketTypes.LoginResponsePacket.authIsOK(data));
        }
    }

    /**
     * 处理电台回复我的参数。0x50数据包
     *
     * @param data 0x50数据包
     */
    public void onReceiveStatusPacket(byte[] data) {
        //if (this.authDone) return;//6100会频繁激活0x50包
        if (IComPacketTypes.ControlPacket.getType(data) == 0x01) return;
        if (IComPacketTypes.StatusPacket.getAuthOK(data)
                && IComPacketTypes.StatusPacket.getIsConnected(data)) {//令牌认证成功，且处于连接状态
            audioUdp.rigPort = IComPacketTypes.StatusPacket.getRigAudioPort(data);
            audioUdp.rigIp = rigIp;
            civUdp.rigPort = IComPacketTypes.StatusPacket.getRigCivPort(data);
            civUdp.rigIp = rigIp;
            Log.e(TAG, String.format("onReceiveStatusPacket: Status packet 0x50: civRigPort:%d,audioRigPort:%d"
                    , civUdp.rigPort, audioUdp.rigPort));
            //todo 6100与icom有差异
            civUdp.startAreYouThereTimer();//civ端口启动连接电台
            audioUdp.startAreYouThereTimer();//audio端口启动连接电台
        }//else处理关闭连接？？？
    }

    /**
     * 处理令牌数据包
     *
     * @param data 0x40数据包
     */
    public void onReceiveTokenPacket(byte[] data) {
        //看是不是续订令牌包
        if (IComPacketTypes.TokenPacket.getRequestType(data) == IComPacketTypes.TOKEN_TYPE_RENEWAL
                && IComPacketTypes.TokenPacket.getRequestReply(data) == 0x02
                && IComPacketTypes.ControlPacket.getType(data) != IComPacketTypes.CMD_RETRANSMIT) {
            int response = IComPacketTypes.TokenPacket.getResponse(data);
            if (response == 0x0000) {//说明续订成功了
                gotAuthOK = true;
            } else if (response == 0xffffffff) {
                remoteId = IComPacketTypes.ControlPacket.getSentId(data);
                localToken = IComPacketTypes.TokenPacket.getTokRequest(data);
                rigToken = IComPacketTypes.TokenPacket.getToken(data);
                sendConnectionRequest();//申请连接
            } else {
                Log.e(TAG, "Token renewal failed,unknow response");
            }
        }
    }

    /**
     * 发送civ指令
     *
     * @param data 指令
     */
    public void sendCivData(byte[] data) {
        civUdp.sendCivData(data);
    }

    /**
     * 发送音频数据到电台
     *
     * @param data 数据
     */
    public void sendWaveData(float[] data) {
        audioUdp.sendTxAudioData(data);
    }

    /**
     * 发送0x90数据包，向电台请求连接
     */
    public void sendConnectionRequest() {
        sendTrackedPacket(IComPacketTypes.ConnInfoPacket.connectRequestPacket((short) 0
                , localId, remoteId, (byte) 0x01, (byte) 0x03, innerSeq, localToken, rigToken
                , rigMacAddress, rigName, userName, IComPacketTypes.AUDIO_SAMPLE_RATE
                , civUdp.getLocalPort(), audioUdp.getLocalPort()
                , IComPacketTypes.TX_BUFFER_SIZE));
        innerSeq++;
    }

    /**
     * 发送登录数据包0x80包
     */
    public void sendLoginPacket() {
        sendTrackedPacket(IComPacketTypes.LoginPacket.loginPacketData((short) 0
                , localId, remoteId, innerSeq, localToken, rigToken, userName, password, APP_NAME));
        innerSeq++;
    }

    @Override
    public void setOnStreamEvents(OnStreamEvents onStreamEvents) {
        super.setOnStreamEvents(onStreamEvents);
        audioUdp.onStreamEvents = onStreamEvents;
        civUdp.onStreamEvents = onStreamEvents;
    }

    /**
     * 启动令牌续订时钟
     */
    public void startTokenTimer() {
        stopTimer(tokenTimer);
        Log.d(TAG, String.format("start Toke Timer: local port:%d,remote port %d", localPort, rigPort));
        tokenTimer = new Timer();
        tokenTimer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                sendTokenPacket(IComPacketTypes.TOKEN_TYPE_RENEWAL);
            }
        }, IComPacketTypes.TOKEN_RENEWAL_PERIOD_MS, IComPacketTypes.TOKEN_RENEWAL_PERIOD_MS);
    }

    public void closeAll() {
        sendTrackedPacket(IComPacketTypes.TokenPacket.getTokenPacketData((short) 0
                , localId, remoteId, IComPacketTypes.TOKEN_TYPE_DELETE, innerSeq, localToken, rigToken));
        innerSeq++;
        this.close();
        civUdp.close();
        audioUdp.stopTXAudio();
        audioUdp.close();

        civUdp.sendOpenClose(false);
    }
}
